/*
 * @Author: 吴世扬 18368095041@163.com
 * @Date: 2024-10-17 15:29:15
 * @LastEditors: 吴世扬 18368095041@163.com
 * @LastEditTime: 2024-10-18 17:22:46
 * @FilePath: /react18-vite-admin/src/components/ui/KeepAlive/KeepAlive.tsx
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import {
    type FC,
    memo,
    type ReactElement,
    type JSXElementConstructor,
    type MemoExoticComponent,
    useEffect
} from 'react';
import { useImmer } from 'use-immer';

import './KeepAlive.scoped.scss';

type MatchPattern = (string | RegExp)[];

interface KeepAliveProps {
    /**
     * 如果指定，则只有与 `include` 名称
     * 匹配的组件才会被缓存。
     */
    include?: MatchPattern;
    /**
     * 任何名称与 `exclude`
     * 匹配的组件都不会被缓存。
     * 未实装
     */
    exclude?: MatchPattern;
    /**
     * 最多可以缓存多少组件实例。
     * 未实装
     */
    max?: number | string;
    /**
     * 需要更新的组件配置，用于刷新缓存
     */
    refresh?: { name: string }; // 使用对象是为了避免同字符串更新时不刷新
}

export type KaReactElement = ReactElement<
    any,
    { kaName?: string } & JSXElementConstructor<any>
>;

const KeepAlive: FC<{ children: KaReactElement } & KeepAliveProps> = ({
    children,
    include,
    refresh
}) => {
    const judgeIsCached = (kaName: string) => {
        return include?.some((item) => {
            if (item instanceof RegExp) {
                return item.test(kaName ?? '');
            }
            return item === kaName;
        });
    };

    const [nodeMap, setNodeMap] = useImmer(
        new Map<string, MemoExoticComponent<() => KaReactElement>>()
    );

    useEffect(() => {
        console.log('刷新')
        if (refresh && nodeMap.has(refresh.name)) {
            setNodeMap((draft) => {
                const MemoNode = nodeMap.get(refresh.name)!;
                draft.set(
                    refresh.name,
                    memo(() => <MemoNode />)
                );
            });
        }
    }, [refresh]);

    useEffect(() => {
        setNodeMap((draft) => {
            for (const key of nodeMap.keys()) {
                if (!judgeIsCached(key)) {
                    // console.log(include,'删除缓存', key);
                    draft.delete(key);
                }
            }
        });
        if (children.type.kaName) {
            const kaName = children.type.kaName;
            if (judgeIsCached(kaName)) {
                const hasCachedNode = nodeMap.has(kaName);
                if (!hasCachedNode) {
                    setNodeMap((draft) => {
                        draft.set(
                            kaName,
                            memo(() => children)
                        );
                    });
                }
            }
        }
    }, [children, include]);

    return (
        <>
            {/* 渲染缓存组件 */}
            {Array.from(nodeMap.entries()).map(([key, Node]) => {
                return (
                    <div
                        data-kaname={key}
                        className="keepalive-item"
                        style={{
                            display:
                                key === children.type.kaName ? 'flex' : 'none'
                        }}
                        key={key}
                    >
                        <Node />
                    </div>
                );
            })}
            {/* 非缓存组件直接渲染 */}
            {children.type.kaName && judgeIsCached(children.type.kaName) ? (
                <></>
            ) : (
                children
            )}
        </>
    );
};

export default memo(KeepAlive);
